package com.zy.crypto;

import com.zy.plugin.EncryptPlugin;
import com.zy.util.EncryptUtil;
import com.zy.vo.ResponseVO;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhy
 * @date 2025/6/19 17:03
 */
@Aspect
@Component
public class CryptoAspect {
    private final ConcurrentHashMap<String, Type[]> typeCache = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, List<Field>> fieldCache = new ConcurrentHashMap<>();
    private static final EncryptPlugin encryptPlugin = new EncryptPlugin();

    @Before("@annotation(com.zy.crypto.EncryptMethod)")
    public void doBefore(JoinPoint joinPoint) throws Exception {
        Object[] args = joinPoint.getArgs();
        if (args == null) {
            return;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取方法的参数列表
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object arg = args[i];
            if (arg == null) {
                continue;
            }
            // 检查参数是否有 @EncryptField 注解
            if (!parameter.isAnnotationPresent(com.zy.crypto.EncryptField.class)) {
                continue;
            }
            processObject(arg, true);
        }
    }

    @AfterReturning(pointcut = "@annotation(com.zy.crypto.EncryptMethod)", returning = "result")
    public Object doAfter(JoinPoint joinPoint, Object result) throws Exception {
        if (result == null) {
            return null;
        }
        if (!(result instanceof ResponseVO)) {
            return result;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取方法的返回值
        Type genericReturnType = method.getGenericReturnType();
        ResponseVO<?> vo = new ResponseVO();
        vo.getData();
        doAfterHandler(genericReturnType, ((ResponseVO<?>) result).getData());
        return result;
    }

    private void doAfterHandler(Type genericReturnType, Object object) throws Exception {
        if (object == null) {
            return;
        }
        if (!(genericReturnType instanceof ParameterizedType)) {
            return;
        }
        if (object instanceof Iterable || object.getClass().isArray()) {
            processObject(object, false);
            return;
        }
        String typeName = genericReturnType.getTypeName();
        // 缓存
        if (!typeCache.containsKey(typeName)) {
            ParameterizedType paramType = (ParameterizedType) genericReturnType;
            Type[] typeArgs = paramType.getActualTypeArguments();
            typeCache.put(typeName, typeArgs);
        }
        // 递归遍历，处理泛型嵌套
        Type[] types = typeCache.get(typeName);
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                typeName = type.getTypeName();
                // 缓存
                if (!fieldCache.containsKey(typeName)) {
                    Type rawType = ((ParameterizedType) type).getRawType();
                    Class<?> clazz = (Class<?>) rawType;
                    Field[] declaredFields = clazz.getDeclaredFields();
                    fieldCache.put(typeName, Arrays.asList(declaredFields));
                }
                List<Field> fieldList = fieldCache.get(typeName);
                for (Field field : fieldList) {
                    Type genericType = field.getGenericType();
                    if (genericType instanceof ParameterizedType) {
                        field.setAccessible(true);
                        Object o = field.get(object);
                        processObject(o, false);
                        doAfterHandler(type, object);
                    }
                }
            } else {
                processObject(object, false);
            }
        }
    }

    private void processObject(Object object, boolean encrypt) throws Exception {
        if (object == null) {
            return;
        }

        // 处理集合或数组
        if (object instanceof Iterable) {
            for (Object item : (Iterable<?>) object) {
                if (item != null) {
                    processObject(item, encrypt);
                }
            }
        } else if (object.getClass().isArray()) {
            for (Object item : Arrays.asList(object)) {
                if (item != null) {
                    processObject(item, encrypt);
                }
            }
        }

        Class<?> clazz = object.getClass();
        String typeName = clazz.getTypeName();
        // 缓存
        if (!fieldCache.containsKey(typeName)) {
            List<Field> fieldList = new ArrayList<>();
            while (clazz != null) {
                Field[] declaredFields = clazz.getDeclaredFields();
                fieldList.addAll(Arrays.asList(declaredFields));
                clazz = clazz.getSuperclass();
            }
            fieldCache.put(typeName, fieldList);
        }
        List<Field> fieldList = fieldCache.get(typeName);
        for (Field field : fieldList) {
            if (!encrypt) {
                if (encryptPlugin.getResponseNullFieldList().stream().anyMatch(x -> x.equals(field.getName()))) {
                    field.setAccessible(true);
                    Object value = field.get(object);
                    if (value == null) {
                        continue;
                    }
                    field.set(object, null);
                    continue;
                }
            }
            if (!field.isAnnotationPresent(com.zy.crypto.EncryptField.class)) {
                continue;
            }
            field.setAccessible(true);
            Object value = field.get(object);
            if (value == null) {
                continue;
            }
            if (value instanceof String) {
                String processedValue = encrypt ?
                        EncryptUtil.encrypt((String) value) :
                        EncryptUtil.decrypt((String) value);
                field.set(object, processedValue);
            }
        }
    }

}
